/*
 * Copyright 2010 elvish render Team
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 * http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef EI_DATAFLOW_H
#define EI_DATAFLOW_H

/** \brief The database system is a memory management subsystem for 
 * processing large or dynamically generated data. 
 * The job manager is a job management subsytem for 
 * multi-threading, network rendering and on-demand 
 * executions(deferred loading/lazy evaluations).
 * \file ei_dataflow.h
 * \author Elvic Liang, Bo Zhou
 */

#include <eiCORE/ei_core.h>
#include <eiCORE/ei_platform.h>
#include <eiCORE/ei_filesys.h>
#include <eiCORE/ei_message.h>
#include <eiCORE/ei_atomic.h>

#ifdef __cplusplus
extern "C" {
#endif

#define EI_DEFAULT_MEMORY_LIMIT			650
#define EI_DEFAULT_FILE_SIZE_LIMIT		512
#define EI_DEFAULT_PURGE_RATE			0.2

/** \brief Is the data flushable, if flushable, we can reuse the memory 
 * of the data when memory limit is hit. */
#define EI_DB_FLUSHABLE		(1 << 0)
/** \brief Always use initialize function to re-generate the data when it's flushed. 
 * if false, we only use initialize function to generate it once, then when 
 * we need rebuilding, we use disk cache to reload it. */
#define EI_DB_GEN_ALWAYS	(1 << 1)
/** \brief Dirty flag for incremental changes. */
#define EI_DB_DIRTY			(1 << 2)
/** \brief Dirty flag for local disk caching. */
#define EI_DB_CHANGED		(1 << 3)
/** \brief Has the data been initialized. */
#define EI_DB_INITED		(1 << 4)
/** \brief Is the data being initialized. */
#define EI_DB_INITING		(1 << 5)
/** \brief Whether data has local copy on current host. once this data 
 * has been received from network, it should have cached a 
 * local copy, and this flag should be set to eiTRUE. */
#define EI_DB_LOCAL			(1 << 6)
/** \brief This flag indicates that we don't re-use the data generated by other 
 * rendering hosts over the network, we always re-generate the data on 
 * local host, so data generated notifications do not need to be sent. */
#define EI_DB_GEN_LOCAL		(1 << 7)
/** \brief Has the data been flushed(paged out) due to memory limit. */
#define EI_DB_FLUSHED		(1 << 8)

/* hard-coded values to be used as checksums */
#define eiTEST_CHECK_SHORT		-12345
#define eiTEST_CHECK_USHORT		45683
#define eiTEST_CHECK_INT		-68474930
#define eiTEST_CHECK_UINT		17384940
#define eiTEST_CHECK_LONG		-324583739917
#define eiTEST_CHECK_ULONG		153747484968
#define eiTEST_CHECK_FLOAT		-230671.6765f
#define eiTEST_CHECK_DOUBLE		1483658.897142


typedef struct eiGlobals			eiGlobals;

typedef struct eiDatabase			eiDatabase;
typedef struct eiData 				eiData;
typedef struct eiDataGen 			eiDataGen;
typedef struct eiDataGenTable 		eiDataGenTable;

typedef struct eiHost				eiHost;
typedef struct eiProcess			eiProcess;
typedef struct eiTLS				eiTLS;
typedef struct eiTlsData			eiTlsData;

typedef struct eiThreadInfo			eiThreadInfo;
typedef struct eiThreadProcDesc 	eiThreadProcDesc;

typedef struct eiBaseWorker			eiBaseWorker;
typedef struct eiExecutor			eiExecutor;
typedef struct eiJobQueue			eiJobQueue;
typedef struct eiMaster				eiMaster;


/** \brief In-place byte-swap data callback.
 * @param data The current data pointer.
 * @param size The current data size in bytes.
 */
typedef void (*ei_db_byteswap)(eiDatabase *db, void *data, const eiUint size);
/** \brief Generate data callback. part of the dataflow architecture. */
typedef void (*ei_db_generate_data)(eiDatabase *db, const eiTag data_tag, eiData *pData, eiTLS *pTls);
/** \brief The data destructor which will be called when deleting the data.
 * we should delete all data allocated in generate_data using local pointers 
 * in this callback. notice that this function will be called multiple times 
 * for all local hosts for each data item, do not delete data referenced by 
 * tags because database will handle that automatically. */
typedef void (*ei_db_clear_data)(eiDatabase *db, void *data);
/** \brief Execute job callback. */
typedef eiBool (*ei_job_execute)(eiDatabase *db, eiBaseWorker *pWorker, void *job, void *param);
/** \brief Count job amount callback, used for updating progress. */
typedef eiUint (*ei_job_count)(eiDatabase *db, void *job);
/** \brief Cast a data type into the current data type. */
typedef eiBool (*ei_cast)(eiDatabase *db, eiByte *dst, const eiByte *src, const eiInt src_type);


/** \brief The global object for each rendering session(can be 
 * either rendering server or rendering manager) which holds 
 * interfaces that can be accessed by other modules. The creator  
 * of this global object is responsible for filling 
 * these interfaces. */
struct eiGlobals {
	eiInterface		*interfaces;
	eiUint			num_interfaces;
};

/** \brief Actually not the data, but the info to the managed data. */
struct eiData {
	/* the lock to prevent from concurrent updating, used when 
	   paging is disabled. for internal use only */
	eiLock				lock;
	/* the address of the data in local memory if 
	   the data was successfully accessed. */
	void				*ptr;
	eiInt				type;
	eiUint				size;
	eiAtomic			flag;
	/* the host that created this data, if it matches local host, 
	   then the data must have local copy, and can be loaded from 
	   file, otherwise, the data must be received from network. */
	eiHostID			host;
	eiFile				*file;			/* internal use only */
	eiUint64			file_offset;	/* internal use only */
	eiUint				file_length;	/* internal use only */
	/* the reference count for scene graph, will 
	   not be modified by the database. */
	eiInt				ref_count;
};

/** \brief Data generator description, including necessary callbacks. */
struct eiDataGen {
	ei_db_byteswap			byteswap;
	ei_db_generate_data		generate_data;
	ei_db_clear_data		clear_data;
	/* the job related functions will be available 
	   when the data is a job. */
	ei_job_execute			execute_job;
	ei_job_count			count_job;
	/* data type conversion function */
	ei_cast					cast;
	/* the size of this data type when using it as element 
	   of data array or data table, etc. for dynamically 
	   growing data, this size is meaningless, and we should 
	   never use varying-sized data as data array element. */
	eiSizet					type_size;
};

/** \brief The table of data generators, user must fill this. */
struct eiDataGenTable {
	eiDataGen		*data_gens;
	eiUint			num_data_gens;
};

/** \brief The procedure used to traverse the job queue, if zero is 
 * returned while traversing jobs, the traversal will be terminated. */
typedef eiInt (*ei_job_queue_proc)(const eiTag job, void *param);

/** \brief Create a job queue. */
eiCORE_API eiJobQueue *ei_create_job_queue();
/** \brief Delete a job queue. */
eiCORE_API void ei_delete_job_queue(eiJobQueue *queue);
/** \brief Initialize a job queue. */
eiCORE_API void ei_job_queue_init(eiJobQueue *queue);
/** \brief Cleanup a job queue. */
eiCORE_API void ei_job_queue_clear(eiJobQueue *queue);
/** \brief Add a new job into the job queue. */
eiCORE_API eiBool ei_job_queue_add(eiJobQueue *queue, const eiTag job);
/** \brief Returns a job and delete it from the job queue, it will 
 * return NULL if there's no more job. */
eiCORE_API eiTag ei_job_queue_find(eiJobQueue *queue);
/** \brief Traverse all jobs in the job queue. */
eiCORE_API void ei_job_queue_traverse(eiJobQueue *queue, ei_job_queue_proc proc, void *param);
/** \brief Returns whether the job queue is empty. */
eiCORE_API eiBool ei_job_queue_empty(eiJobQueue *queue);


/*---------------------- database ----------------------*/

/** \brief Initialize the global object.
 */
eiCORE_API void ei_globals_init(eiGlobals *globals);

/** \brief Cleanup the global object.
 */
eiCORE_API void ei_globals_exit(eiGlobals *globals);

/** \brief Create a database instance. for internal use only.
 * Construct a shared memory database, which can be shared among mutiple 
 * processors and cores within a single machine.
 * @param memory_limit The maximum memory size can be used in megabytes(MB), 
 * if memory_limit is set to 0, it means no paging.
 * @param file_size_limit The maximum size of each cache file in megabytes(MB).
 * @param purge_rate The rate to purge when memory fills up, range from 0.0 - 1.0.
 * @param executor The executor for multi-threaded job execution.
 * @param master The master for network rendering manager.
 */
eiCORE_API eiDatabase *ei_create_db(const eiIntptr memory_limit, 
									   const eiSizet file_size_limit, 
									   const eiGeoScalar purge_rate, 
									   eiExecutor * const executor, 
									   eiMaster * const master);

/** \brief Delete a database instance. for internal use only.
 */
eiCORE_API void ei_delete_db(eiDatabase *db);

/** \brief Perform garbage collection, delete all unreleased data.
 */
eiCORE_API void ei_db_gc(eiDatabase *db);

/** \brief Return TLS of the database. for internal use only.
 */
eiCORE_API eiTLS *ei_db_get_tls(eiDatabase *db);

/** \brief Get file system of this database. this function provides the access to the 
 * global file system which could be used for file operations with generic purposes.
 */
eiCORE_API eiFileSystem * const ei_db_file_system(eiDatabase *db);

/** \brief Set a table of data generators, the first data generator with data type 0 must 
 * be located at index 0, the second data generator with data type 1 must be located 
 * at index 1, and so on. the database system will not be responsible for freeing 
 * up the memory of the table, users must handle that.
 * @param data_gen_table The table of data generators.
 */
eiCORE_API void ei_db_data_gen_table(eiDatabase *db, 
									 const eiDataGenTable *data_gen_table);

/** \brief Set the global object, this can be used to pass something like global 
 * renderer to generate_data callbacks.
 */
eiCORE_API void ei_db_globals(eiDatabase *db, 
							  eiGlobals *globals);

/** \brief Get interface by interface ID from the global object.
 */
eiCORE_API eiInterface ei_db_globals_interface(eiDatabase *db, 
											   const eiInt interface_id);

/** \brief Call data generator to do byte-swapping for data according to its type.
 * for internal use only.
 */
eiCORE_API void ei_db_byteswap_typed(eiDatabase *db, 
						  const eiInt type, 
						  void *data, 
						  const eiUint size);

/** \brief Force cast a data type into another, do the data conversion. 
 * it's not recommended to use this function. */
eiCORE_API void ei_db_force_cast(eiDatabase *db, 
								 eiByte *dst, const eiInt dst_type, 
								 const eiByte *src, const eiInt src_type);

/** \brief Cast a data type into another, do the data conversion. */
eiCORE_API void ei_db_cast(eiDatabase *db, 
						   eiByte *dst, const eiInt dst_type, 
						   const eiByte *src, const eiInt src_type);

/** \brief Get data element size in bytes by type.
 */
eiCORE_API eiSizet ei_db_type_size(eiDatabase *db, 
								   const eiInt type);

/** \brief Execute a user job in the context of current thread. */
eiCORE_API eiBool ei_db_execute_job_on_current_thread(
	eiDatabase *db, 
	const eiTag job, 
	void *param);

/** \brief Execute a user job queue in the context of current thread. */
eiCORE_API eiBool ei_db_execute_jobs_on_current_thread(
	eiDatabase *db, 
	eiJobQueue *jobs, 
	void *param);

/** \brief Adjust memory limit in megabytes(MB).
 * @param memory_limit The new memory limit in megabytes(MB).
 */
eiCORE_API void ei_db_mem_limit(eiDatabase *db, 
								   const eiIntptr memory_limit);

/** \brief Receive a data from the host that generated the data over network.
 * @param defer_init If true, the sender will defer the initialization, the 
 * receiver may need to initialize the data; otherwise, the send will initialize 
 * the data, the data that receiver gets is guaranteed to be initialized.
 */
eiCORE_API void ei_db_net_recv_from_host(eiDatabase *db, 
										const eiTag tag, 
										eiData *pData, 
										eiTLS *pTls, 
										const eiBool defer_init);

/** \brief Returns the executor associated with this database. */
eiCORE_API eiExecutor *ei_db_executor(eiDatabase *db);

/** \brief Returns the rendering manager of this database.
 * returns NULL if the current host is a rendering server. */
eiCORE_API eiMaster *ei_db_net_master(eiDatabase *db);

/** \brief Returns whether current rendering host is a rendering manager.
 * if it returns false, the current host is a rendering server.
 */
eiCORE_API eiBool ei_db_net_is_master(eiDatabase *db);

/** \brief Create custom file chunk from file for a data item.
 */
eiCORE_API void ei_db_create_file_chunk_from_file(eiDatabase *db, 
									   eiData *pData, 
									   const char *filename, 
									   const eiFileMode mode);

/** \brief Create a data item, returns its local memory address, this 
 * function calls ei_db_access implicitly, users must explicitly call 
 * ei_db_end when their editing is done.
 * @param tag The tag returned to identify the created data.
 * @param type The type of the data.
 * @param size The size of the data.
 * @param flag The creation flags of the data, the following flags are supported:
 * EI_DB_FLUSHABLE
 * EI_DB_GEN_ALWAYS
 */
eiCORE_API eiByte *ei_db_create(eiDatabase *db, 
								   eiTag * const tag, 
								   const eiInt type, 
								   eiUint size, 
								   const eiInt flag);

/** \brief Resize a data item. we can create data with an initial size, then resize 
 * the data in data generator upon the first access. no matter expanding or shrinking 
 * the space, data content will be copied as much as possible. this function must be 
 * called within a pair of ei_db_access/ei_db_end. this function returns the new 
 * resized data pointer, client should update its pointer with that.
 * @tag The tag of the data item to be resized.
 * @newsize The new size of the data item.
 */
eiCORE_API eiByte *ei_db_resize(eiDatabase *db, 
								   const eiTag tag, 
								   eiUint newsize);

/** \brief Re-allocate a data item, which calls ei_db_access/ei_db_end implicitly.
 * @tag The tag of the data item to be re-allocated.
 * @newsize The new size of the data item.
 */
eiCORE_API eiByte *ei_db_realloc(eiDatabase *db, 
								 const eiTag tag, 
								 const eiUint newsize);

/** \brief Begin accessing a data item by its tag, returns the data info pointer.
 * @param tag The tag of the data item to be accessed.
 */
eiCORE_API eiData *ei_db_access_info(eiDatabase *db, 
										const eiTag tag);

/** \brief Begin accessing a data item by its tag, returns the data memory pointer, 
 * data is only valid within a pair of ei_db_access/ei_db_end calls.
 * @param tag The tag of the data item to be accessed.
 */
eiCORE_API eiByte *ei_db_access(eiDatabase *db, 
								   const eiTag tag);

/** \brief Begin accessing a data item by its tag, returns the data info pointer.
 * if the initialization is needed, it will be deferred to the next access time.
 * @param tag The tag of the data item to be accessed.
 */
eiCORE_API eiData *ei_db_access_info_defer_init(eiDatabase *db, 
												const eiTag tag);

/** \brief Begin accessing a data item by its tag without affecting the 
 * initialization state, if the initialization is needed, it will be 
 * deferred to the next access time. */
eiCORE_API eiByte *ei_db_access_defer_init(eiDatabase *db, 
								   const eiTag tag);

/** \brief End accessing a data item by its tag.
 * @param tag The tag of the data item to be end accessed.
 */
eiCORE_API void ei_db_end(eiDatabase *db, 
									const eiTag tag);

/** \brief Add reference to a data item without actual access to the data content, 
 * return the current reference count of the data item after the referencing. 
 * this function is designed for reference counted life-time management.
 */
eiCORE_API eiInt ei_db_ref(eiDatabase *db, const eiTag tag);

/** \brief Release reference to a data item without actual access to the data 
 * content, return the current reference count of the data item after the 
 * unreferencing. this function is designed for reference counted life-time 
 * management.
 */
eiCORE_API eiInt ei_db_unref(eiDatabase *db, const eiTag tag);

/** \brief Return the current reference count of the data item. this function 
 * is designed for reference counted life-time management.
 */
eiCORE_API eiInt ei_db_getref(eiDatabase *db, const eiTag tag);

/** \brief Delete a data item by its tag.
 * @param tag The tag of the data item to be deleted.
 */
eiCORE_API void ei_db_delete(eiDatabase *db, 
								const eiTag tag);

/** \brief Dirt a data item by its tag, the data item will be re-read over the network 
 * and re-initialized. this function is to be used by the scene pre-processing module 
 * on rendering manager only to notify changes of the scene to all rendering hosts. 
 * all data in scene graph which have been changed by scene description APIs should 
 * use this function to invalidate, and all generated data from these changed data 
 * should be invalidated too.
 * @param tag The tag of the data item to be flagged dirty.
 */
eiCORE_API void ei_db_dirt(eiDatabase *db, 
							  const eiTag tag);

/** \brief Flush all dirty data items with all hosts. this function is only designed 
 * for pre-processng, do not use it in multi-threaded rendering process.
 */
eiCORE_API void ei_db_flush_dirty(eiDatabase *db);

/** \brief Force flush a data item by its tag no matter whether it's dirty, notifying 
 * all hosts that the data was changed. all writes will only take effect on other 
 * hosts after flushing.
 * @param tag The tag of the data item to be force flushed.
 */
eiCORE_API void ei_db_flush(eiDatabase *db, 
							   const eiTag tag);

/** \brief Get the type of a data by its tag.
 * @param tag The tag of the data item whose type will be returned.
 */
eiCORE_API eiInt ei_db_type(eiDatabase *db, 
								const eiTag tag);

/** \brief Get the size of a data by its tag. be aware that this value may not be 
 * up-to-date due to network rendering, because we won't receive up-to-date data 
 * size over the network in this function.
 * @param tag The tag of the data item whose size will be returned.
 */
eiCORE_API eiUint ei_db_size(eiDatabase *db, 
								const eiTag tag);

/** \brief Get physical memory usage peak in bytes.
 */
eiCORE_API eiIntptr ei_db_mem_peak(eiDatabase *db);

/** \brief Get virtual memory usage peak in bytes.
 */
eiCORE_API eiIntptr ei_db_virtual_mem_peak(eiDatabase *db);

/** \brief Get page file size peak in bytes.
 */
eiCORE_API eiIntptr ei_db_pagefile_peak(eiDatabase *db);


/*---------------------- job manager ----------------------*/

/** \brief The callback for initializing global object, which is 
 * usually a renderer. */
typedef void (*eiInitGlobals)(eiGlobals *globals, eiDatabase *db);
/** \brief The callback for cleaning up global object. */
typedef void (*eiExitGlobals)(eiGlobals *globals, eiDatabase *db);

/** \brief The callback for initializing custom thread local storage. */
typedef void (*eiInitTLS)(eiTLS *tls);
/** \brief The callback for cleaning up custom thread local storage. */
typedef void (*eiExitTLS)(eiTLS *tls);

/** \brief The callback for setting global scene. */
typedef eiBool (*eiSetSceneCallback)(eiGlobals *globals, eiDatabase *db, const eiTag scene_tag);
/** \brief The callback for ending editing global scene. */
typedef eiBool (*eiEndSceneCallback)(eiGlobals *globals, eiDatabase *db);
/** \brief The callback for updating editing global scene. */
typedef eiBool (*eiUpdateSceneCallback)(eiGlobals *globals, eiDatabase *db);
/** \brief The callback for linking a module. */
typedef eiBool (*eiLinkCallback)(eiGlobals *globals, eiDatabase *db, const char *module_name);

eiCORE_API eiHostID ei_tls_get_host(eiTLS *tls);
eiCORE_API void ei_tls_allocate_interfaces(eiTLS *tls, const eiUint num_interfaces);
eiCORE_API void ei_tls_clear_interfaces(eiTLS *tls);
eiCORE_API eiInterface ei_tls_get_interface(eiTLS *tls, const eiUint interfaceId);
eiCORE_API void ei_tls_set_interface(eiTLS *tls, const eiUint interfaceId, eiInterface iface);
eiCORE_API void ei_tls_free_interface(eiTLS *tls, const eiUint interfaceId);
eiCORE_API void *ei_tls_allocate(eiTLS *tls, eiSizet size);
eiCORE_API void ei_tls_free(eiTLS *tls, void *ptr);

eiCORE_API eiThreadID ei_tls_get_thread_id(eiTLS *tls);
eiCORE_API eiGeoScalar ei_tls_get_cache_hit_rate(eiTLS *tls);

/** \brief Run status of base worker and executor.
 */
enum {
	EI_NO_ERROR = 0,		/* default return value */
	EI_RUN_SUCCESS,			/* run success */
	EI_RUN_FAILURE,			/* generic failure */
	EI_INVALID_THREAD_INFO,	/* invalid thread info */
	EI_INVALID_EXECUTOR,	/* invalid executor */
	EI_INVALID_WORKER,		/* invalid worker */
	EI_INVALID_TLS,			/* invalid TLS */
	EI_UNKNOWN_EVENT_TYPE,	/* unknown event type */
};

void ei_base_worker_init(eiBaseWorker *worker);
void ei_base_worker_exit(eiBaseWorker *worker);

eiHostID ei_base_worker_get_host_id(eiBaseWorker *worker);
eiThreadID ei_base_worker_get_thread_id(eiBaseWorker *worker);
eiUint ei_base_worker_get_progress(eiBaseWorker *worker);

eiCORE_API eiBool ei_base_worker_is_running(eiBaseWorker *worker);
eiCORE_API void ei_base_worker_step_progress(eiBaseWorker *worker, const eiUint count);

/** \brief Executor type */
enum {
	EI_EXECUTOR_TYPE_NONE = 0, 
	EI_EXECUTOR_TYPE_MANAGER,		/* rendering manager */
	EI_EXECUTOR_TYPE_SERVER,		/* rendering server */
	EI_EXECUTOR_TYPE_COUNT, 
};

typedef void (*eiTraverseTLSCallback)(eiTLS *tls, void *param);

/** \brief Create an executor for multi-threaded executions. */
eiCORE_API eiExecutor *ei_create_exec(const eiInt executor_type, eiInitTLS init_tls);
/** \brief Delete an executor */
eiCORE_API void ei_delete_exec(eiExecutor *exec, eiExitTLS exit_tls);

/** \brief Get thread local storage associated with the calling thread. */
eiCORE_API eiTLS *ei_exec_get_tls(eiExecutor *exec);
/** \brief Get the current number of finished jobs. */
eiCORE_API eiInt ei_exec_get_num_finished_jobs(eiExecutor *exec);
/** \brief Get the current number of failed jobs. */
eiCORE_API eiInt ei_exec_get_num_failed_jobs(eiExecutor *exec);
/** \brief Traverse all TLS. */
eiCORE_API void ei_exec_traverse_tls(eiExecutor *exec, eiTraverseTLSCallback cb, void *param);

/** \brief The callback for being notified that one job started, 
 * users can write custom message in msg, the message will be 
 * automatically passed to ei_process_update callback.
 */
typedef eiBool (*ei_process_one_job_started)(eiProcess *process, 
											 const eiTag job, 
											 const eiHostID hostId, 
											 const eiThreadID threadId, 
											 eiMessage *msg);
/** \brief The callback for being notified that one job finished, 
 * users can write custom message in msg, the message will be 
 * automatically passed to ei_process_update callback.
 */
typedef eiBool (*ei_process_one_job_finished)(eiProcess *process, 
											  const eiTag job, 
											  const eiHostID hostId, 
											  const eiThreadID threadId, 
											  eiMessage *msg);
/** \brief The callback for being notified that one worker finished, 
 * users can write custom message in msg, the message will be 
 * automatically passed to ei_process_update callback.
 */
typedef eiBool (*ei_process_one_worker_finished)(eiProcess *process, 
												 eiBaseWorker *pWorker, 
												 eiMessage *msg);
/** \brief The callback for handling rendering progress.
 * @param percent The percentage of current job completion.
 */
typedef eiBool (*ei_process_progress)(eiProcess *process, 
									  const eiScalar percent);
/** \brief The serialized message popped from message queue, this 
 * callback is guaranteed to be called only within main thread. 
 * users can respond to the message they are interested in.
 */
typedef void (*ei_process_update)(eiProcess *process, 
									const eiMessage *msg);

/** \brief The description of a rendering process, 
 * users can override the callbacks to handle certain 
 * rendering messages.
 */
struct eiProcess {
	ei_process_one_job_started		one_job_started;
	ei_process_one_job_finished		one_job_finished;
	ei_process_one_worker_finished	one_worker_finished;
	ei_process_progress				progress;
	ei_process_update				update;
};

/** \brief Create a master instance represents a rendering manager. */
eiCORE_API eiMaster *ei_create_master();
/** \brief Delete a master instance. */
eiCORE_API void ei_delete_master(eiMaster *master);

/** \brief Add a new rendering server to the rendering manager.
 * @param host_name The host name or IP address string of the 
 * rendering server.
 * @param The port number on which the rendering server is listening.
 */
eiCORE_API void ei_master_add_host(eiMaster *master, 
									  const char *host_name, 
									  const eiUshort port_number);

/** \brief The executor must be set before running process. */
eiCORE_API void ei_master_set_executor(eiMaster *master, 
										  eiExecutor *pExecutor);
/** \brief The process must be set before running process. */
eiCORE_API void ei_master_set_process(eiMaster *master, 
										 eiProcess *pProcess);
/** \brief The database must be set before running process. */
eiCORE_API void ei_master_set_database(eiMaster *master, 
										  eiDatabase *pDatabase);

/** \brief Create workers for rendering manager.
 * @param nthreads The number of threads to launch on local rendering 
 * manager for processing rendering jobs. if value <= 0 is specified, 
 * the rendering manager will determine the number automatically.
 * @param distributed Whether to distribute rendering jobs to remote 
 * rendering servers.
 */
eiCORE_API void ei_master_create_workers(eiMaster *master, 
										 const eiInt nthreads, 
										 const eiBool distributed, 
										 eiInitTLS init_tls);

/** \brief Add a new job into the job queue. */
eiCORE_API eiBool ei_master_add_job(eiMaster *master, 
									   const eiTag job);

/** \brief Returns whether the rendering is running, for checking abort. */
eiBool ei_master_is_running(eiMaster *master);
eiUint ei_master_get_progress(eiMaster *master);

/** \brief Run process until all rendering jobs are completed.
 * @param local_only If true, the master only executes jobs on current local host.
 */
eiCORE_API void ei_master_run_process(eiMaster *master, const eiBool local_only);

/** \brief A custom message handling procedure. */
typedef eiBool (*eiMessageProc)(eiDatabase *db, SOCKET sock, void *param);

/** \brief Broadcast a message to all hosts, excluding a specific host.
 */
eiCORE_API void ei_master_broadcast(eiMaster *master, 
									eiMessage *msg, 
									const eiHostID excl_host, 
									eiMessageProc proc, 
									void *param);

/** \brief Launch a rendering server for processing incoming 
 * rendering jobs over the network.
 * @param data_gen_table The table of custom data generators.
 * @param init_globals The callback to initialize global objects.
 * @param exit_globals The callback to cleanup global objects.
 * @param init_tls The callback to initialize thread local storage.
 * @param exit_tls The callback to cleanup thread local storage.
 * @param set_scene_callback The callback for setting global scene.
 * @param end_scene_callback The callback for end editing global scene.
 * @param link_callback The callback for linking a module.
 */
eiCORE_API void ei_job_run_server(const eiDataGenTable *data_gen_table, 
								  eiInitGlobals init_globals, 
								  eiExitGlobals exit_globals, 
								  eiInitTLS init_tls, 
								  eiExitTLS exit_tls, 
								  eiSetSceneCallback set_scene_callback, 
								  eiEndSceneCallback end_scene_callback, 
								  eiUpdateSceneCallback update_scene_callback, 
								  eiLinkCallback link_callback);


#ifdef __cplusplus
}
#endif
 
#endif
